/////////////////////////////////////////////////////////////////////////////////////////////
///<Filename>  		SceneSystem.cpp		<\Filename>
///<Date>      		09/26/2011			<\Date>      
///<Author>   		Richard A. Small	<\Author>   
///<Purpose>   	
///		Defines the SceneSystem Class
///<\Purpose>
/////////////////////////////////////////////////////////////////////////////////////////////
#include "SceneSystem.h"

/// <Summary> The nullsceneid </Summary>
const HashString SceneSystem::NULLsceneID("NULLscene");

/// <Summary> The instance of this class </Summary>
SceneSystem* SceneSystem::instance = NULL;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Gets the instance of this class </Summary>
///
/// <Return>	The instance of this class </Return>
////////////////////////////////////////////////////////////////////////////////////////////////////
SceneSystem& SceneSystem::GetInstance()
{
	if(NULL == instance)
	{
		instance = new SceneSystem;
	}
	return *instance;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Deletes the instance of this class </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::DeleteInstance()
{
	SAFE_DELETE(instance);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Initialize </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::Initialize()
{

}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Shutdown </Summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::Shutdown()
{
	for(unsigned i = 0; i < sceneStack.GetSize(); ++i)
	{
		sceneStack[i]->Exit();
	}

	sceneStack.Clear();

	for(SceneRegisterMap::Iterator iter(registeredScenes); !iter.End(); ++iter)
	{
		delete (*iter).data;
	}

	registeredScenes.Clear();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Change scene </Summary>
///
/// <Param name="ID">   	
/// 	The identity of the scene you wish to change too
/// </Param>
/// <Param name="delay">
/// 	(optional) The amount of time you wish to delay this command 
/// </Param>
/// <Param name="flush">	
///		(optional) Value whether or not you want all other current commands to stop execution 
///	</Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::ChangeScene(const HashString& ID, float delay, bool flush)
{
	if(flush)
	{
		cmdQueue.Clear();
	}

	SendCmd(CHANGE, &ID, delay);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Push scene </Summary>
///
/// <Param name="ID">   	
/// 	The identity of the scene you wish to push
/// </Param>
/// <Param name="delay">
/// 	(optional) The amount of time you wish to delay this command 
/// </Param>
/// <Param name="flush">	
///		(optional) Value whether or not you want all other current commands to stop execution 
///	</Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::PushScene(const HashString& ID, float delay, bool flush)
{
	if(flush)
	{
		cmdQueue.Clear();
	}

	SendCmd(PUSH, &ID, delay);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Pop scene </Summary>
///
/// <Param name="numToPop">   	
/// 	(optional) The number of scenes you wish to pop
/// </Param>
/// <Param name="delay">
/// 	(optional) The amount of time you wish to delay this command 
/// </Param>
/// <Param name="flush">	
///		(optional) Value whether or not you want all other current commands to stop execution 
///	</Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::PopScene(int numToPop, float delay, bool flush)
{
	if(flush)
	{
		cmdQueue.Clear();
	}

	while(numToPop > 0)
	{
		SendCmd(POP, NULL, delay);
		numToPop--;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Process the commands currently inside the queue </Summary>
///
/// <Param name="delta">	Delta time value </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::ProcessCmdQueue(float delta)
{
	for(CommandQueue::Iterator iter(cmdQueue); !iter.End(); ++iter)
	{
		(*iter).delay -= delta;
	}

	CommandQueue::Iterator iter(cmdQueue);
	while(cmdQueue.GetSize() > 0)
	{
		iter.BeginHead();

		if((*iter).delay > 0.0f)
			break;

		GameSceneCmd cmd = cmdQueue.Pop_Front();

		IScene* previousScene = GetCurrentScene();

		switch(cmd.cmd)
		{
		case PUSH:
			{
				if(previousScene != NULL)
					GetCurrentScene()->Override();

				sceneStack.Push_Back(registeredScenes[cmd.sceneID->GetIdentity()]);

				if(previousScene == NULL)
					GetCurrentScene()->Enter(SceneSystem::NULLsceneID);
				else
					GetCurrentScene()->Enter(previousScene->GetSceneID());
			}
			break;
		case POP:
			{
				if(previousScene != NULL)
				{
					GetCurrentScene()->Exit();

					sceneStack.Pop_Back();

					if(GetCurrentScene() != NULL)
						GetCurrentScene()->Resume(previousScene->GetSceneID());
				}
			}
			break;
		case CHANGE:
			{
				while(CurrentStackSize() > 0)
				{
					GetCurrentScene()->Exit();

					sceneStack.Pop_Back();
				}

				sceneStack.Push_Back(registeredScenes[cmd.sceneID->GetIdentity()]);

				if(previousScene == NULL)
					GetCurrentScene()->Enter(SceneSystem::NULLsceneID);
				else
					GetCurrentScene()->Enter(previousScene->GetSceneID());
			}
			break;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Updates the scenes on the stack and calls input on the topmost scene </Summary>
///
/// <Param name="delta">	The delta. </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::Update(float delta)
{
	ProcessCmdQueue(delta);

	if(GetCurrentScene() == NULL)
		return;

	GetCurrentScene()->Input(delta);

	for(unsigned i = sceneStack.GetSize(); i > 0; --i)
	{
		sceneStack[i - 1]->Update(delta);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <Summary>	Renders the scenes on the stack </Summary>
///
/// <Param name="delta">	The delta. </Param>
////////////////////////////////////////////////////////////////////////////////////////////////////
void SceneSystem::Render(float delta)
{
	for(unsigned i = 0; i < sceneStack.GetSize(); ++i)
	{
		sceneStack[i]->Render(delta);
	}
}